/**
 * CPAR TOOLKIT - Compiler and Runtime for CPAR
 * Copyright (C) 2012 CPAR TOOLKIT Project Developers
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include <windows.h>
#include "cpar.h"

static void CPARPointerArrayAllocatorListener( CPARAllocator *allocator, void *data);

struct CPARPointerArray
{
  char          **data;
  long          size;
  long          growSize;
  long          count;
  CPARAllocator *allocator;
  long          listenerIdx;
};

static
long
CPARPointerArrayExpand(CPARPointerArray *array, long howMany)
{
  char  **pNewData;
  long  toGrow;
  long  r;

  r = 0;

  if(howMany > array->growSize)
  {
    toGrow = howMany / array->growSize * howMany;
  }else
    toGrow = array->growSize;

  pNewData = (char **)CPARAllocatorAlloc(array->allocator, sizeof(char*) * (toGrow + array->size));
  if(NULL == pNewData)
    return -1;

  if(array->data)
  {
    CopyMemory( (void*)pNewData, (void*)array->data, sizeof(char*) * array->count );
    CPARAllocatorFree(array->allocator, array->data);
    array->data   = pNewData;
    array->size   += toGrow;
  }else{
    array->data   = pNewData;
    array->size   += toGrow;
    CPARAllocatorDestroyListenerAdd( array->allocator, CPARPointerArrayAllocatorListener, array );
  }

  return 0;
}

static void
CPARPointerArrayAllocatorListener( CPARAllocator *allocator, void *data)
{
  CPARPointerArrayDestroy((CPARPointerArray*)data);
}

long CPARPointerArrayCreate( CPARAllocator *allocator, CPARPointerArray **array, long initialSize, long growSize )
{
  CPARPointerArray  *pArray;

  pArray = (CPARPointerArray*) CPARAllocatorAlloc(allocator,sizeof(CPARPointerArray));
  if(NULL == pArray)
    return -1;

  pArray->data      = 0;
  pArray->count     = 0;
  pArray->size      = 0;
  pArray->growSize  = growSize;
  pArray->allocator = allocator;

  pArray->listenerIdx = -1;
  
  *array = pArray;
  return CPARPointerArrayExpand(pArray,initialSize);
}

long CPARPointerArrayCount( CPARPointerArray *array )
{
  return array->count;
}

long CPARPointerArrayAdd( CPARPointerArray *array, void *pointer, long *idx )
{
  long  r;

  if(array->count == array->size)
  {
    r = CPARPointerArrayExpand(array,1);
    if(r)
      return r;
  }

  array->data[array->count] = (char*)pointer;
  if(idx)
    *idx = array->count;

  array->count;
  return 0;
}

void* CPARPointerArrayRemove( CPARPointerArray *array, long idx )
{
  char  *pData;

  pData = array->data[idx];
  CopyMemory(array->data + idx, array->data + idx + 1, array->count - idx);
  
  return (void*) pData;
}

void CPARPointerArrayDestroy( CPARPointerArray *array )
{
  CPARAllocatorDestroyListenerRemove(array->allocator, array->listenerIdx);

  CPARAllocatorFree(array->allocator, array->data);
  CPARAllocatorFree(array->allocator, (void*) array);
}

void **CPARPointerArrayGetData(CPARPointerArray *array)
{
  return (void**) array->data;
}

cpar_status CPARPointerArrayPush( CPARPointerArray *array, void *pointer)
{
  return CPARPointerArrayAdd(array,pointer,NULL);
}

void* CPARPointerArrayPop( CPARPointerArray *array )
{
  return CPARPointerArrayRemove(array,array->count-1);
}